`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          CLK_PRD                 = 5;
localparam          CMD_LEN                 = 16;   // command bits length, include R/W(1bit) + Address(CMD_LEN-1 bits)
localparam          DATA_LEN                = 8;    // data bits length
localparam          RW_POS                  = 23;   // read/write indication bit position 
localparam          RD_IND                  = 1'b1; // read indication, if spi_frm[RW_POS]==RD_IND menas read.
localparam          CLK_DIV                 = 4;    // CLK_DIV>=2
localparam          CPOL                    = 1'b1; // SCK is 1'b0:LOW; 1'b1:HIGH; when idle
localparam          CPHA                    = 1'b1; // capture SDO/SDI at 1'b0:1st; 1'b1:2nd SCK edge
localparam          FRM_LEN                 = CMD_LEN + DATA_LEN;

reg                                         rst_n;
reg                                         clk;

wire                                        csn;
wire                                        sck;
wire                                        sdo;
wire                                        sdoe;       // output enable for tristate
reg                                         sdi;

reg                                         spi_en;
wire                [FRM_LEN-1:0]           spi_frm;       // cmd+wdata from CPU to sdo
wire                                        spi_frm_fwe;   // forward write enable
wire                [FRM_LEN-1:0]           spi_frm_bwd;   // rdata from sdi to CPU
wire                                        spi_frm_bwe;   // backward write enable
wire                                        spi_busy;

reg                                         cs;
reg                                         re;
reg                                         we;
reg                 [FRM_LEN-1:0]           wd;

initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_spi", 1);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:GEN_RW
    cs = 1'b0;
    we = 1'b0;
    re = 1'b0;
    wd = 0;

    spi_en = 1'b0;
    @(posedge rst_n);
    @(posedge clk);

    spi_wr_t(5, 23'h5abc, 8'hD0);
    spi_rd_t(0, 23'h0123);

    #1_000;
    rgrs.one_chk_done("gen is done.");
end

initial begin:GEN_SDI
    sdi = 0;

    @(posedge rst_n);

    forever begin
        if (CPOL==CPHA)
            @(negedge sck);
        else
            @(posedge sck);

        sdi <=`U_DLY $urandom();
    end
end

spi_rw_reg #(
        .CMD_LEN                        (CMD_LEN                        ),	// command bits length, include R/W(1bit) + Address(CMD_LEN-1 bits)
        .DATA_LEN                       (DATA_LEN                       ),	// data bits length
        .RW_POS                         (RW_POS                         ),	// read/write indication bit position 
        .RD_IND                         (RD_IND                         ),	// read indication, if spi_frm[RW_POS]==RD_IND menas read.
        .CLK_DIV                        (CLK_DIV                        ),	// CLK_DIV>=2
        .CPOL                           (CPOL                           ),	// SCK is 1'b0:LOW; 1'b1:HIGH; when idle
        .CPHA                           (CPHA                           ),	// capture SDO/SDI at 1'b0:1st; 1'b1:2nd SCK edge
        .FRM_LEN                        (FRM_LEN                        )
) u_spi_rw_reg ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .csn                            (csn                            ),
        .sck                            (sck                            ),
        .sdo                            (sdo                            ),
        .sdoe                           (sdoe                           ),	// output enable for tristate
        .sdi                            (sdi                            ),

        .spi_en                         (spi_en                         ),
        .spi_frm                        (spi_frm                        ),	// cmd+wdata from CPU to sdo
        .spi_frm_fwe                    (spi_frm_fwe                    ),	// forward write enable
        .spi_frm_bwd                    (spi_frm_bwd                    ),	// rdata from sdi to CPU
        .spi_frm_bwe                    (spi_frm_bwe                    ),	// backward write enable
        .spi_busy                       (spi_busy                       )
);

bid_rw_reg #(
        .DW                             (FRM_LEN                        )
) u_spi_frm ( 
        .rst_cfg_n                      (rst_n                          ),
        .clk                            (clk                            ),

        .cs                             (cs                             ),
        .re                             (re                             ),
        .we                             (we                             ),
        .wd                             (wd                             ),

        .bwe                            (spi_frm_bwe                    ),	// backward(hardware to software) write enable
        .bwd                            (spi_frm_bwd                    ),	// backward(hardware to software) write data
        .q                              (spi_frm                        ),
        .fwe                            (spi_frm_fwe                    ),	// forward(software to hardware) write enable
        .fre                            (                               )	// forward(software to hardware) read enable
);

task spi_wr_t;
    input integer           gap;
    input [CMD_LEN-2:0]     addr;
    input [DATA_LEN-1:0]    data;
    
    repeat(gap) begin
        @(posedge clk);
    end
    spi_en <=`U_DLY 1'b1;

    cs <=`U_DLY 1'b1;
    we <=`U_DLY 1'b1;
    wd <=`U_DLY {~RD_IND, addr, data};
    @(posedge clk);
    cs <=`U_DLY 1'b0;
    we <=`U_DLY 1'b0;
    @(posedge clk);
    while(spi_busy==1'b1) begin
        @(posedge clk);
    end
    spi_en <=`U_DLY 1'b0;
endtask

task spi_rd_t;
    input integer           gap;
    input [CMD_LEN-2:0]     addr;
    
    repeat(gap) begin
        @(posedge clk);
    end
    spi_en <=`U_DLY 1'b1;

    cs <=`U_DLY 1'b1;
    we <=`U_DLY 1'b1;
    wd <=`U_DLY {RD_IND, addr, {DATA_LEN{1'b0}}};
    @(posedge clk);
    cs <=`U_DLY 1'b0;
    we <=`U_DLY 1'b0;
    @(posedge clk);
    while(spi_busy==1'b1) begin
        @(posedge clk);
    end
    spi_en <=`U_DLY 1'b0;
endtask

endmodule

